﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using UserSurface;
using ApplicationCore;

namespace EnzymeLaborTool
{
    public partial class ProcessingCtrl : UserControl
    {
        MainSurface _surface;
        EnzymeDBDataContext _dataContext;
        private System.Windows.Forms.Timer searchTimer = new System.Windows.Forms.Timer();
        private DateTime _minDate = new DateTime(1753, 1, 1);
        private DateTime _maxDate = new DateTime(9999, 12, 31);

        public string SearchTerm
        {
            get
            {
                return filterBatchesTextBox.Text;
            }
        }

        public BatchView SelectedBatch
        {
            get
            {
                return batchBindingSource.Current as BatchView;
            }
        }

        public ProcessingCtrl()
        {
            InitializeComponent();
            searchTimer.Tick += new EventHandler(searchTimer_Tick);
        }

        void searchTimer_Tick(object sender, EventArgs e)
        {
            searchTimer.Stop(); //stop after one tick
            BindBatches();
        }

        private bool _isInitialized = false;
        public void Init(MainSurface ms)
        {
            if (!_isInitialized)
            {
                _surface = ms;
                procInfoBindingSource.DataSource = _surface.ProcInfo;
                rawdataGridView.CellFormatting += new DataGridViewCellFormattingEventHandler(rawdataGridView_CellFormatting);
                procDataGridView.CellFormatting += new DataGridViewCellFormattingEventHandler(rawdataGridView_CellFormatting);
                rawdataGridView.CurrentCellChanged += new EventHandler(rawdataGridView_CurrentCellChanged);

                ApplySecurity();
                // InvalidateDataContext();
                _isInitialized = true;
            }
        }

        public void InvalidateDataContext(BatchView batch = null)
        {
            int batchPosition = batchBindingSource.Position;
            int analytePosition = analyteBindingSource.Position;
            var batchSortCol = batchesDataGridView.SortedColumn;
            var batchSortOrder = batchesDataGridView.SortOrder;

            _dataContext = _surface.GetDataContext();
            batchBindingSource.CurrentChanged -= batchBindingSource_CurrentChanged;
            analyteBindingSource.CurrentChanged -= analyteBindingSource_CurrentChanged;
            BindBatches();
            batchBindingSource.CurrentChanged += batchBindingSource_CurrentChanged;
            analyteBindingSource.CurrentChanged += analyteBindingSource_CurrentChanged;


            //    analyteBindingSource_CurrentChanged(null, null);

            //restore sorting and positon
            if (batchSortCol != null)
            {
                switch (batchSortOrder)
                {
                    case SortOrder.Ascending:
                        {
                            batchesDataGridView.Sort(batchSortCol, ListSortDirection.Ascending);
                            break;
                        }

                    case SortOrder.Descending:
                        {
                            batchesDataGridView.Sort(batchSortCol, ListSortDirection.Descending);
                            break;
                        }
                    default:
                        break;
                }
            }
            if (batch == null)
            {
                if (batchBindingSource.Position != batchPosition && batchPosition > -1)
                {
                    batchBindingSource.Position = batchPosition;
                }
                else
                {
                    batchBindingSource_CurrentChanged(null, null);
                }
            }
            else if (batch != null)
            {
                int position = batchBindingSource.IndexOf(batch);
                if (batchBindingSource.Position != position && position > -1)
                {
                    batchBindingSource.Position = position;
                }
                else
                {
                    batchBindingSource_CurrentChanged(null, null);
                }
            }
            else
            {
                batchBindingSource_CurrentChanged(null, null);
            }
            analyteBindingSource.Position = analytePosition;
        }

        public void NavigateToBatch(string filterTerm, BatchView batch)
        {
            filterBatchesTextBox.TextChanged -= new EventHandler(OnFilterBatches);
            filterBatchesTextBox.Text = filterTerm;
            filterBatchesTextBox.TextChanged += new EventHandler(OnFilterBatches);

            InvalidateDataContext(batch);
            //int position = batchBindingSource.IndexOf(batch);
            //batchBindingSource.Position = position;
        }

        void rawdataGridView_CurrentCellChanged(object sender, EventArgs e)
        {
            if (rawdataGridView.CurrentCell != null)
            {
                int colIndex = rawdataGridView.CurrentCell.OwningColumn.Index;
                int rowIndex = rawdataGridView.CurrentCell.OwningRow.Index;
                int i;
                int j;
                if (_surface.CurrenBatchLayoutB.LayoutType == "SPLIT")
                {
                    if (rowIndex > _surface.CurrenBatchLayoutB.LayoutRowCount - 1) i = rowIndex - _surface.CurrenBatchLayoutB.LayoutRowCount; else i = rowIndex;
                }
                else
                {
                    i = rowIndex;
                }
                j = colIndex;

                procInputHeader.Values.Description = _surface.CurrenBatchLayoutB.Layout[i, j];
            }
        }

        void rawdataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            try
            {
                if (_surface.CurrenBatchLayoutB.IsEmpty)
                    return;
                int c = e.ColumnIndex;
                int r = e.RowIndex;
                if (_surface.CurrenBatchLayoutB.LayoutType == "SPLIT" && r > _surface.CurrenBatchLayoutB.LayoutRowCount - 1)
                {
                    r = r - _surface.CurrenBatchLayoutB.LayoutRowCount;
                }
                string entry = _surface.CurrenBatchLayoutB.Layout[r, c];
                e.CellStyle.BackColor = _surface.CurrenBatchLayoutB.Entry2Color(entry);
                if (e.Value != null && e.Value.ToString().StartsWith("#"))
                {
                    e.CellStyle.ForeColor = SystemColors.GrayText;
                    e.CellStyle.Font = new Font(this.Font, FontStyle.Italic);
                }
                else
                {
                    e.CellStyle.ForeColor = SystemColors.WindowText;
                    e.CellStyle.Font = new Font(this.Font, FontStyle.Regular);
                }
            }
            catch (Exception ex) 
            {
                Microsoft.SqlServer.MessageBox.ExceptionMessageBox box = new Microsoft.SqlServer.MessageBox.ExceptionMessageBox(ex);
                box.Show(this);
            }
        }

        private bool forceDisableSaveRawData = false;
        private bool forceDisableDeleteRawData = false;
        private void ApplySecurity()
        {
            pasteRawDataToolStripMenuItem.Enabled = false;
            excludeRawDataToolStripMenuItem.Enabled = false;
            includeRawDataToolStripMenuItem.Enabled = false;
            provedCheckBox.Enabled = false;
            saveRawDataButton.Enabled = false;
            forceDisableSaveRawData = true;
            forceDisableDeleteRawData = true;
            deleteRawDataButton.Enabled = false;

            if (_surface.SecDescriptor.Rights.ContainsKey("RawData"))
            {
                foreach (TableRight tr in _surface.SecDescriptor.Rights["RawData"])
                {
                    switch (tr)
                    {
                        case TableRight.Insert:
                            {
                                pasteRawDataToolStripMenuItem.Enabled = true;
                                excludeRawDataToolStripMenuItem.Enabled = true;
                                includeRawDataToolStripMenuItem.Enabled = true;
                                provedCheckBox.Enabled = true;
                                saveRawDataButton.Enabled = true;
                                forceDisableSaveRawData = false;
                                break;
                            }
                        case TableRight.Delete:
                            {
                                //batchDataGridView.AllowUserToDeleteRows = true;
                                //batchDeleteItem.Enabled = true;
                                forceDisableDeleteRawData = false;
                                deleteRawDataButton.Enabled = true;
                                break;
                            }
                        case TableRight.Update:
                            {
                                excludeRawDataToolStripMenuItem.Enabled = true;
                                includeRawDataToolStripMenuItem.Enabled = true;
                                provedCheckBox.Enabled = true;
                                saveRawDataButton.Enabled = true;
                                forceDisableSaveRawData = false;
                                break;
                            }
                    }
                }
            }
        }

        void analyteBindingSource_CurrentChanged(object sender, EventArgs e)
        {
            EnzymeView drv = analyteBindingSource.Current as EnzymeView;
            _surface.NavigateEnzyme(drv);
            procInfoBindingSource.ResetCurrentItem();
            BindRawData();
        }

        void batchBindingSource_CurrentChanged(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("batchBindingSource_CurrentChanged called");
            BatchView bv = batchBindingSource.Current as BatchView;
            _surface.NavigateBatchB(bv);
            DataGridHelper.Configure(procDataGridView, _surface.CurrenBatchLayoutB, false);
            procInfoBindingSource.ResetCurrentItem();

            BindAnalytes();
            BindProtocol();
            BindProtocolSettings();


            //BindRawData(); // is done by BindAnalyte -> analyteBindingSource_CurrentChanged
        }

        private void ValidateLayout()
        {
            rawdataGridView.Enabled = _surface.CurrenBatchLayoutB.ValidateAll();
            procDataGridView.Enabled = rawdataGridView.Enabled;
            invLayoutLabel.Visible = !rawdataGridView.Enabled;
        }

        private void BindRawData()
        {
            DateTime start = DateTime.Now;
            DataGridHelper.Configure(rawdataGridView, _surface.CurrenBatchLayoutB, true);
            ValidateLayout();
            if (LoadRawData())
            {
                Process();
            }
            else
            {
                ClearProcVals();
            }

            rawdataGridView_CurrentCellChanged(null, null);
            System.Diagnostics.Debug.WriteLine("Total data load time {0}s", (DateTime.Now - start).TotalSeconds);
        }

        private bool LoadRawData()
        {
            bool proved = false;
            string[,] data = _surface.LoadRawData(_surface.CurrenBatchLayoutB, _surface.ProcInfo, out proved);
            provedCheckBox.Checked = proved;
            if (data != null)
            {
                DataGridHelper.LoadData(rawdataGridView, _surface.CurrenBatchLayoutB, data);
                saveRawDataButton.Enabled = true && !forceDisableSaveRawData;
                deleteRawDataButton.Enabled = true && !forceDisableDeleteRawData;
                return true;
            }
            else
            {
                data = new string[_surface.CurrenBatchLayoutB.PlateRowCount, _surface.CurrenBatchLayoutB.PlateColumnCount];
                DataGridHelper.LoadData(rawdataGridView, _surface.CurrenBatchLayoutB, data);
                saveRawDataButton.Enabled = false;
                deleteRawDataButton.Enabled = false;
                return false;
            }
        }

        private bool ValidateRawDataMatrix()
        {
            foreach (DataGridViewRow r in rawdataGridView.Rows)
            {
                for (int j = 0; j < rawdataGridView.ColumnCount; j++)
                {
                    if (r.Cells[j].Value == null || String.IsNullOrEmpty(r.Cells[j].Value.ToString()))
                        return false;
                }
            }
            return true;
        }


        private void BindAnalytes()
        {
            BatchView bv = batchBindingSource.Current as BatchView;
            if (bv != null)
            {
                if (bv.LayoutType == "FULL")
                {
                    analyteBindingSource.DataSource = _dataContext.EnzymeViews.Where(x => x.Type == "Metabolite").OrderBy(x => x.Code);
                }
                else
                {
                    analyteBindingSource.DataSource = _dataContext.EnzymeViews.Where(x => x.Type == "Enzyme").OrderBy(x => x.Code);
                }
            }
            else
            {
                analyteBindingSource.DataSource = null;
            }
        }

        private void BindBatches()
        {
            var q = from x in _dataContext.BatchViews select x;
            if (!string.IsNullOrEmpty(filterBatchesTextBox.Text))
            {

                string[] filterParts = filterBatchesTextBox.Text.Split("-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                StringBuilder sb = new StringBuilder();
                switch (filterParts.Length)
                {
                    case 1:
                        {
                            int number;
                            DateTime date;

                            if (DateTime.TryParse(filterParts[0], out date) && date.Date > _minDate && date.Date < _maxDate)
                            {
                                q = from x in q where x.Date == date.Date select x;
                            }
                            else if (Int32.TryParse(filterParts[0], out number))
                            {
                                q = from x in q where x.Number == number select x;
                            }
                            else
                            {
                                q = from x in q where x.Name.ToLower().Contains(filterParts[0].ToLower()) select x;
                            }
                            break;
                        }
                    case 2:
                        {
                            int number;
                            if (Int32.TryParse(filterParts[1], out number))
                                q = from x in q
                                    where x.Name.ToLower().Contains(filterParts[0].ToLower())
                                    where x.Number == number
                                    select x;
                            else
                                goto case 1;
                            break;
                        }
                    case 3:
                        {
                            DateTime date;
                            if (DateTime.TryParse(filterParts[2], out date))
                            {
                                int number;
                                if (Int32.TryParse(filterParts[1], out number))
                                    q = from x in q
                                        where x.Name.ToLower().Contains(filterParts[0].ToLower())
                                        where x.Number == number
                                        where x.Date == date.Date
                                        select x;
                                else
                                    goto case 1;
                                break;

                            }
                            else
                                goto case 2;
                        }
                    default:
                        break;
                }
            }
            batchBindingSource.DataSource = from x in q orderby x.Name, x.Date, x.Number select x;
        }

        private void BindProtocol()
        {
            BatchView bv = batchBindingSource.Current as BatchView;
            if (bv != null)
            {
                protocolBindingSource.DataSource = _dataContext.ProtocolViews.SingleOrDefault(x => x.UId == bv.ProtocolUId);
                kryptonHeaderGroupProtocol.ValuesPrimary.Description = bv.ProtocolName;
            }
            else
            {
                kryptonHeaderGroupProtocol.ValuesPrimary.Description = "";
                //protocolBindingSource.DataSource = null;
            }
        }

        private void BindProtocolSettings()
        {
            BatchView bv = batchBindingSource.Current as BatchView;
            if (bv != null)
            {
                protocolSettingViewBindingSource.DataSource = from x in _dataContext.ProtocolSettingViews
                                                              where x.ProtocolUId == bv.ProtocolUId
                                                              orderby x.ParameterName
                                                              select x;
            }
            else
                protocolSettingViewBindingSource.DataSource = null;
        }

        private void OnFilterBatches(object sender, EventArgs e)
        {
            searchTimer.Start();
        }


        private bool ConfirmDataDelete()
        {
            return MessageBox.Show("Are you sure you want to delete the current raw/processed data?", "Confirm data delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                == DialogResult.Yes;
        }

        private void pasteRawDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                if (Clipboard.ContainsText())
                {
                    string text = Clipboard.GetText();
                    string[,] content = ParseClipboardContent(text);
                    DataGridHelper.LoadData(rawdataGridView, _surface.CurrenBatchLayoutB, content);
                    Process();
                    saveRawDataButton.Enabled = ValidateRawDataMatrix() && !forceDisableSaveRawData;
                    deleteRawDataButton.Enabled = ValidateRawDataMatrix() && !forceDisableDeleteRawData;
                }
            }
            catch (Exception ex)
            {
                Microsoft.SqlServer.MessageBox.ExceptionMessageBox box = new Microsoft.SqlServer.MessageBox.ExceptionMessageBox(ex);
                box.Show(this);
            }
        }

        private void Process()
        {
            double? stderr = null;
            double? allBatchBlankAvg = default(double?);
            double? allBatchMaxAvg = default(double?);
            double blankAvg;
            double maxAvg = double.NaN;

            if (rawdataGridView.Enabled)
            {
                Processor proc = new Processor();
                double?[,] procVals;
                DateTime start = DateTime.Now;

                if (_surface.CurrenBatchLayoutB.LayoutType == "SPLIT")
                {
                    procVals = proc.ProcessEnzymeData(_surface.CurrenBatchLayoutB, GetRawDataMatrixFromGrid(), _surface.ProcInfo.SloapRatio, _surface.ProcInfo.Activation, out stderr, out blankAvg, out maxAvg);
                }
                else
                {
                    procVals = proc.ProcessMetaboliteData(_surface.CurrenBatchLayoutB, GetRawDataMatrixFromGrid(), _surface.ProcInfo.Activation, out stderr, out blankAvg);
                }
                System.Diagnostics.Debug.WriteLine("Pure processing time: {0}s", (DateTime.Now - start).TotalSeconds);
                for (int i = 0; i < procVals.GetLength(0) && i < procDataGridView.RowCount; i++)
                {
                    for (int j = 0; j < procVals.GetLength(1); j++)
                    {
                        procDataGridView[j, i].Value = procVals[i, j];
                        procDataGridView[j, i].Style.Format = "N2";
                    }
                }

                if (_surface.CurrenBatchLayoutB.LayoutType == "SPLIT")
                {
                    allBatchBlankAvg = _surface.BatchBlankAvg();
                    allBatchMaxAvg = _surface.BatchMaxAvg();
                }
                else
                {
                    allBatchBlankAvg = _surface.BatchAvg();
                    allBatchMaxAvg = null;
                }

                if (!allBatchBlankAvg.HasValue)
                {
                    kryptonLabelBlankDiff.Values.ExtraText = "no data available";
                }
                else
                {
                    kryptonLabelBlankDiff.Values.ExtraText = Math.Round(((allBatchBlankAvg.Value - blankAvg) / allBatchBlankAvg.Value) * 100, 2).ToString() + "%";
                }

                if (!allBatchMaxAvg.HasValue)
                {
                    kryptonLabelMaxDiff.Values.ExtraText = "no data available";
                }
                else
                {
                    kryptonLabelMaxDiff.Values.ExtraText = Math.Round(((allBatchMaxAvg.Value - maxAvg) / allBatchMaxAvg.Value) * 100, 2).ToString() + "%";
                }

                if (!allBatchBlankAvg.HasValue || !allBatchMaxAvg.HasValue)
                {
                    kryptonLabelMaxBlankDiff.Values.ExtraText = "no data available";
                }
                else
                {
                    double max_blank = allBatchMaxAvg.Value - allBatchBlankAvg.Value;
                    max_blank = max_blank - (maxAvg - blankAvg);
                    max_blank = (max_blank / (allBatchMaxAvg.Value - allBatchBlankAvg.Value)) * 100;
                    kryptonLabelMaxBlankDiff.Values.ExtraText = Math.Round(max_blank, 2).ToString() + "%";
                }
            }
            else
            {
                kryptonLabelBlankDiff.Values.ExtraText = "--";
                kryptonLabelMaxDiff.Values.ExtraText = "--";
                kryptonLabelMaxBlankDiff.Values.ExtraText = "--";
                ClearProcVals();
            }

            if (stderr.HasValue)
            {
                kryptonLabelStdError.Values.ExtraText = Math.Round(stderr.Value, 2).ToString();
            }
            else
                kryptonLabelStdError.Values.ExtraText = "--";

        }

        private void ClearProcVals()
        {
            for (int i = 0; i < procDataGridView.RowCount; i++)
            {
                for (int j = 0; j < procDataGridView.ColumnCount; j++)
                {
                    procDataGridView[j, i].Value = null;
                }
            }
            kryptonLabelStdError.Values.ExtraText = "--";
            kryptonLabelBlankDiff.Values.ExtraText = "--";
            kryptonLabelMaxDiff.Values.ExtraText = "--";
            kryptonLabelMaxBlankDiff.Values.ExtraText = "--";
        }

        private string[,] GetRawDataMatrixFromGrid()
        {
            string[,] ret = new string[rawdataGridView.RowCount, rawdataGridView.ColumnCount];
            int i = 0;
            foreach (DataGridViewRow r in rawdataGridView.Rows)
            {
                for (int j = 0; j < rawdataGridView.ColumnCount; j++)
                {
                    if (r.Cells[j].Value != null)
                        ret[i, j] = r.Cells[j].Value.ToString().Trim();
                }
                i++;
            }
            return ret;
        }

        private string[,] ParseClipboardContent(string content)
        {
            string[] lines = content.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            int maxColumn = lines.Select(x => x.Split('\t').Length).Max();
            string[,] res = new string[lines.Length, maxColumn];

            for (int i = 0; i < lines.Length; i++)
            {
                string[] line = lines[i].Split('\t');
                for (int j = 0; j < maxColumn; j++)
                {
                    res[i, j] = line[j];
                }
            }
            return res;
        }

        private void excludeRawDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewCell dgvc in rawdataGridView.SelectedCells)
            {
                string s = dgvc.Value.ToString();
                if (!s.StartsWith("#"))
                {
                    dgvc.Value = "#" + s;
                }
            }
            Process();
        }

        private void includeRawDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewCell dgvc in rawdataGridView.SelectedCells)
            {
                dgvc.Value = dgvc.Value.ToString().Replace("#", "");
            }
            Process();
        }

        private void saveRawDataButton_Click(object sender, EventArgs e)
        {
            double x;
            double y;
            try
            {
                this.Cursor = Cursors.WaitCursor;
                double? stderr;
                Processor proc = new Processor();

                double?[,] procVals;
                if (_surface.CurrenBatchLayoutB.LayoutType == "SPLIT")
                {
                    procVals = proc.ProcessEnzymeData(_surface.CurrenBatchLayoutB, GetRawDataMatrixFromGrid(), _surface.ProcInfo.SloapRatio, _surface.ProcInfo.Activation, out stderr, out x, out y);
                }
                else
                {
                    procVals = proc.ProcessMetaboliteData(_surface.CurrenBatchLayoutB, GetRawDataMatrixFromGrid(), _surface.ProcInfo.Activation, out stderr, out x);
                }
                _surface.SaveRawData(_surface.ProcInfo, provedCheckBox.Checked, GetRawDataMatrixFromGrid(), procVals);
                _surface.SaveProcessedValues(_surface.ProcInfo, procVals);
                MessageBox.Show("Data succesfully saved", "Save", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Save", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }

        private void deleteRawDataButton_Click(object sender, EventArgs e)
        {
            if (ConfirmDataDelete())
            {
                _surface.DeleteData();
                if (LoadRawData())
                    Process();
                else
                    ClearProcVals();
            }
        }
    }
}
